{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"https://raw.githubusercontent.com/Qiskit/qiskit-tutorials/master/images/qiskit-heading.png\" alt=\"Note: In order for images to show up in this jupyter notebook you need to select File => Trusted Notebook\" width=\"500 px\" align=\"left\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Repetition codes on real devices\n",
    "\n",
    "* Requires qiskit-ignis 0.2.0 or greater.\n",
    "\n",
    "### Contributors\n",
    "\n",
    "James R. Wootton, IBM Research"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In [another notebook](../../qiskit/ignis/repetition_code.ipynb) we saw how to use the `RepetitionCode` and `GraphDecoder` tools from Ignis. Here we'll specifcially look at how to use them with real hardware."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from qiskit.ignis.verification.topological_codes import RepetitionCode\n",
    "from qiskit.ignis.verification.topological_codes import GraphDecoder\n",
    "from qiskit.ignis.verification.topological_codes import lookuptable_decoding, postselection_decoding"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's start off easy, with a `d=3`, `T=1` repetition code."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "d = 3\n",
    "T = 1\n",
    "code = RepetitionCode(d,T)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The circuits for this are found in `code.circuit['0']` (for an encoded `0`) and code.circuit['1'] (for an encoded `1`). We can use `count_ops` to see which operations these contain."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'cx': 4, 'measure': 5, 'reset': 2, 'barrier': 1}"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "code.circuit['0'].count_ops()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'x': 3, 'barrier': 2, 'cx': 4, 'measure': 5, 'reset': 2}"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "code.circuit['1'].count_ops()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Both contain 4 `cx` gates, which is exactly what we'd expect from this code.\n",
    "\n",
    "Now let's set up a real device as a backend, and compile these circuits for it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "from qiskit import IBMQ\n",
    "from qiskit.compiler import transpile\n",
    "from qiskit.transpiler import PassManager\n",
    "\n",
    "provider = IBMQ.load_account()\n",
    "backend = provider.get_backend('ibmq_16_melbourne')\n",
    "qc0 = transpile(code.circuit['0'], backend=backend)\n",
    "qc1 = transpile(code.circuit['1'], backend=backend)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's see what has happened to the gates they contain."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "gates for encoded 0 =  {'u2': 20, 'cx': 11, 'measure': 5, 'reset': 2, 'barrier': 1}\n",
      "gates for encoded 1 =  {'u3': 3, 'barrier': 2, 'u2': 20, 'cx': 11, 'measure': 5, 'reset': 2}\n"
     ]
    }
   ],
   "source": [
    "print('gates for encoded 0 = ', qc0.count_ops())\n",
    "print('gates for encoded 1 = ', qc1.count_ops())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The single qubit gates are now all `u2`s and `u3`s, as usually happens when we compile to real hardware. But the number of `cx` gates has increased! This implies some remapping has occurred, which is not what we want for error correction. To avoid this, we first need to provide look at the coupling map of the device."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[1, 0], [1, 2], [2, 3], [4, 3], [4, 10], [5, 4], [5, 6], [5, 9], [6, 8], [7, 8], [9, 8], [9, 10], [11, 3], [11, 10], [11, 12], [12, 2], [13, 1], [13, 12]]\n"
     ]
    }
   ],
   "source": [
    "coupling_map = backend.configuration().coupling_map\n",
    "print(coupling_map)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can think of the qubits of a repetition code as sitting along a line (alternating between 'code' and 'link' qubits), with each qubit interacting only with its neighbours. So we need to look at the coupling map and find a line that covers as many qubits as we can. One possibility is"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "line = [0,1,2,3,4,5,6,8,9,10,11,12,13]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "With this we can set up an `initial_layout` dictionary, which tells us exactly which qubit in the circuit corresponds to which qubits on the device."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{Qubit(QuantumRegister(3, 'code_qubit'), 0): 0, Qubit(QuantumRegister(3, 'code_qubit'), 1): 2, Qubit(QuantumRegister(3, 'code_qubit'), 2): 4, Qubit(QuantumRegister(2, 'link_qubit'), 0): 1, Qubit(QuantumRegister(2, 'link_qubit'), 1): 3}\n"
     ]
    }
   ],
   "source": [
    "def get_initial_layout(code,line):\n",
    "    initial_layout = {}\n",
    "    for j in range(d):\n",
    "        initial_layout[code.code_qubit[j]] = line[2*j]\n",
    "    for j in range(d-1):\n",
    "        initial_layout[code.link_qubit[j]] = line[2*j+1]\n",
    "    return initial_layout\n",
    "\n",
    "initial_layout = get_initial_layout(code,line)\n",
    "\n",
    "print(initial_layout)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's compile using this."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "gates for d = 3 with encoded 0: {'u2': 6, 'cx': 4, 'measure': 5, 'reset': 2, 'barrier': 1}\n",
      "gates for d = 3 with encoded 1: {'u3': 3, 'barrier': 2, 'u2': 6, 'cx': 4, 'measure': 5, 'reset': 2}\n"
     ]
    }
   ],
   "source": [
    "qc0 = transpile(code.circuit['0'], backend=backend, initial_layout=initial_layout)\n",
    "qc1 = transpile(code.circuit['1'], backend=backend, initial_layout=initial_layout)\n",
    "\n",
    "print('gates for d = '+str(d)+' with encoded 0:', qc0.count_ops())\n",
    "print('gates for d = '+str(d)+' with encoded 1:', qc1.count_ops())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The number of `cx` gates is now as it should be.\n",
    "\n",
    "Before running, we need to remove the reset gates. For `T=1`, they don't actually do anything anyway. But since they aren't currently supported on hardware, they may cause use trouble."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "def remove_reset(qc):\n",
    "    qc.data = [ gate for gate in qc.data if gate[0].name!='reset' ]\n",
    "    return qc\n",
    "qc0 = remove_reset(qc0)\n",
    "qc1 = remove_reset(qc1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we can actually run the circuits. So let's set up a function to do this for us."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "from qiskit import execute, Aer\n",
    "from qiskit.providers.aer import noise\n",
    "\n",
    "def get_syndrome(circuits,backend,sim=False,shots=8192):\n",
    "    \n",
    "    if sim:\n",
    "        noise_model = noise.device.basic_device_noise_model(backend.properties())\n",
    "        job = execute( circuits, Aer.get_backend('qasm_simulator'), noise_model=noise_model, shots=shots )\n",
    "    else:\n",
    "        job = execute( circuits, backend, shots=shots )\n",
    "    raw_results = {}\n",
    "    for log in ['0','1']:\n",
    "        raw_results[log] = job.result().get_counts(log)\n",
    "\n",
    "    return code.process_results( raw_results )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This has a `sim` argument, with which we can choose whether to actually use the real device, or just use the noise model we get from the device in a simulation.\n",
    "\n",
    "Let's just simulate for now."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "sim = True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'0': {'1 0  01 11': 39, '1 1  00 11': 27, '0 1  01 00': 81, '0 0  01 10': 410, '1 1  01 10': 14, '0 1  00 10': 39, '1 0  00 01': 58, '1 0  11 01': 24, '1 1  01 01': 4, '0 0  10 01': 94, '1 1  10 10': 2, '1 1  00 00': 5, '0 1  10 11': 25, '1 0  10 11': 35, '1 0  00 10': 421, '0 0  10 10': 534, '0 0  11 00': 231, '0 1  01 11': 31, '0 1  00 01': 207, '1 1  11 11': 2, '0 0  01 01': 389, '1 0  01 00': 34, '0 1  11 01': 11, '1 1  10 01': 5, '0 1  10 00': 5, '0 0  11 11': 65, '0 1  11 10': 19, '0 0  00 11': 711, '1 0  10 00': 176, '0 0  00 00': 4468, '1 0  11 10': 26}, '1': {'1 0  01 11': 37, '0 0  01 10': 12, '1 1  01 10': 378, '0 1  00 10': 435, '0 0  10 01': 17, '1 1  01 01': 388, '0 1  10 11': 33, '1 0  00 10': 49, '0 0  10 10': 3, '0 1  01 11': 41, '0 1  00 01': 56, '0 0  11 11': 1, '1 1  11 00': 302, '1 0  11 10': 9, '1 1  00 11': 667, '0 1  01 00': 48, '1 0  00 01': 178, '1 0  11 01': 14, '1 1  10 10': 586, '1 1  00 00': 4291, '1 0  10 11': 37, '0 0  11 00': 3, '1 1  11 11': 80, '0 0  01 01': 3, '1 0  01 00': 115, '0 1  11 01': 18, '1 1  10 01': 98, '0 1  10 00': 206, '0 1  11 10': 43, '0 0  00 11': 27, '1 0  10 00': 14, '0 0  00 00': 3}}\n"
     ]
    }
   ],
   "source": [
    "results = get_syndrome([qc0,qc1],backend,sim=sim)\n",
    "print(results)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And we can decode the results."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "d = 3 ,log = 0\n",
      "logical error probability for matching      = 0.0352783203125\n",
      "logical error probability for lookup table  = 0.03857421875\n",
      "logical error probability for postselection = 0.0011178180192264698\n",
      "\n",
      "d = 3 ,log = 1\n",
      "logical error probability for matching      = 0.0411376953125\n",
      "logical error probability for lookup table  = 0.0361328125\n",
      "logical error probability for postselection = 0.0006986492780624127\n",
      "\n"
     ]
    }
   ],
   "source": [
    "dec = GraphDecoder(code)\n",
    "\n",
    "logical_prob_match = dec.get_logical_prob(results)\n",
    "logical_prob_lookup = lookuptable_decoding(results,results)\n",
    "logical_prob_post = postselection_decoding(results)\n",
    "\n",
    "for log in ['0','1']:\n",
    "    print('d =',d,',log =',log)\n",
    "    print('logical error probability for matching      =',logical_prob_match[log])\n",
    "    print('logical error probability for lookup table  =',logical_prob_lookup[log])\n",
    "    print('logical error probability for postselection =',logical_prob_post[log])\n",
    "    print('')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's see what happens when we look at different code sizes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "gates for d = 3 with encoded 0: {'u2': 6, 'cx': 4, 'measure': 5, 'barrier': 1} \n",
      "\n",
      "d = 3 ,log = 0\n",
      "logical error probability for matching      = 0.03466796875\n",
      "logical error probability for lookup table  = 0.0379638671875\n",
      "logical error probability for postselection = 0.0004439511653718091\n",
      "\n",
      "d = 3 ,log = 1\n",
      "logical error probability for matching      = 0.034423828125\n",
      "logical error probability for lookup table  = 0.0301513671875\n",
      "logical error probability for postselection = 0.0009170105456212746\n",
      "\n",
      "\n",
      "gates for d = 4 with encoded 0: {'u2': 12, 'cx': 6, 'measure': 7, 'barrier': 1} \n",
      "\n",
      "d = 4 ,log = 0\n",
      "logical error probability for matching      = 0.0247802734375\n",
      "logical error probability for lookup table  = 0.0179443359375\n",
      "logical error probability for postselection = 0.0\n",
      "\n",
      "d = 4 ,log = 1\n",
      "logical error probability for matching      = 0.020751953125\n",
      "logical error probability for lookup table  = 0.015869140625\n",
      "logical error probability for postselection = 0.0\n",
      "\n",
      "\n",
      "gates for d = 5 with encoded 0: {'u2': 12, 'cx': 8, 'measure': 9, 'barrier': 1} \n",
      "\n",
      "d = 5 ,log = 0\n",
      "logical error probability for matching      = 0.017333984375\n",
      "logical error probability for lookup table  = 0.0086669921875\n",
      "logical error probability for postselection = 0.0\n",
      "\n",
      "d = 5 ,log = 1\n",
      "logical error probability for matching      = 0.0157470703125\n",
      "logical error probability for lookup table  = 0.0062255859375\n",
      "logical error probability for postselection = 0.0\n",
      "\n",
      "\n",
      "gates for d = 6 with encoded 0: {'u2': 12, 'cx': 10, 'measure': 11, 'barrier': 1} \n",
      "\n",
      "d = 6 ,log = 0\n",
      "logical error probability for matching      = 0.010009765625\n",
      "logical error probability for lookup table  = 0.0013427734375\n",
      "logical error probability for postselection = 0.0\n",
      "\n",
      "d = 6 ,log = 1\n",
      "logical error probability for matching      = 0.0108642578125\n",
      "logical error probability for lookup table  = 0.001953125\n",
      "logical error probability for postselection = 0.0\n",
      "\n",
      "\n",
      "gates for d = 7 with encoded 0: {'u2': 12, 'cx': 12, 'measure': 13, 'barrier': 1} \n",
      "\n",
      "d = 7 ,log = 0\n",
      "logical error probability for matching      = 0.007568359375\n",
      "logical error probability for lookup table  = 0.000244140625\n",
      "logical error probability for postselection = 0.0\n",
      "\n",
      "d = 7 ,log = 1\n",
      "logical error probability for matching      = 0.0091552734375\n",
      "logical error probability for lookup table  = 0.0003662109375\n",
      "logical error probability for postselection = 0.0\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "for d in range(3,8):\n",
    "    \n",
    "    code = RepetitionCode(d,1)\n",
    "    \n",
    "    initial_layout = get_initial_layout(code,line)\n",
    "    circuits = [ transpile(code.circuit[log], backend=backend, initial_layout=initial_layout) for log in ['0','1'] ]\n",
    "    circuits = [ remove_reset(qc) for qc in circuits ]\n",
    "    \n",
    "    print('gates for d = '+str(d)+' with encoded 0:', circuits[0].count_ops(), '\\n')\n",
    "\n",
    "    results = get_syndrome(circuits,backend,sim=sim)   \n",
    "    \n",
    "    dec = GraphDecoder(code)\n",
    "\n",
    "    logical_prob_match = dec.get_logical_prob(results)\n",
    "    logical_prob_lookup = lookuptable_decoding(results,results)\n",
    "    logical_prob_post = postselection_decoding(results)\n",
    "\n",
    "    for log in ['0','1']:\n",
    "        print('d =',d,',log =',log)\n",
    "        print('logical error probability for matching      =',logical_prob_match[log])\n",
    "        print('logical error probability for lookup table  =',logical_prob_lookup[log])\n",
    "        print('logical error probability for postselection =',logical_prob_post[log])\n",
    "        print('')\n",
    "    print('')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we look only at odd `d`, here we see a steady decrease in the logical error probability, as expected. A decrease is not seen between each odd $d$ and the following $d+1$ for the matching decoder. This is due to the fact that the number of errors required to overturn a clear majority is the same in both cases."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that all the jobs above could have been sent in a single batch, which speeds things up for real devices. Also, two separate sets of results for each code should really be obtained and used in `lookuptable_decoding` to prevent over-fitting. These things were not done to keep this a simpler and clearer tutorial. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "keywords = {'Topics': ['Ignis', 'Quantum error correction'], 'Commands': ['`RepetitionCode`', '`GraphDecoder`', '`transpile`', '`initial_layout`']}"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
